package citylife.geotools.demo;

import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.util.Collection;
import java.util.concurrent.CountDownLatch;

import org.geotools.geometry.jts.ReferencedEnvelope;
import org.geotools.tile.Tile;
import org.geotools.tile.TileService;
import org.geotools.tile.util.AsyncTileLayer;
import org.opengis.referencing.FactoryException;
import org.opengis.referencing.operation.TransformException;

public class Class8WmtsAsyncTileLayer extends AsyncTileLayer {

  private CountDownLatch countDownLatch;

  public Class8WmtsAsyncTileLayer(TileService service) {
    super(service);
  }

  @Override
  protected void renderTiles(Collection<Tile> tiles, Graphics2D g2d, ReferencedEnvelope viewportExtent, AffineTransform worldToImageTransform) {

    this.countDownLatch = new CountDownLatch(tiles.size());

    localRenderTiles(tiles, g2d, viewportExtent, worldToImageTransform);
    try {
      this.countDownLatch.await();
    } catch (InterruptedException ie) {
      java.util.logging.Logger.getGlobal().log(java.util.logging.Level.INFO, "", ie);
    }
    this.countDownLatch = null;
  }

  @Override
  protected void renderTile(final Tile tile, final Graphics2D g2d, final double[] points) {

    Runnable r = () -> {
      BufferedImage img = getTileImage(tile);

      g2d.drawImage(img, (int) points[0], (int) points[1], (int) Math.ceil(points[2] - points[0]), (int) Math.ceil(points[3] - points[1]), null);

      countDownLatch.countDown();
    };
    new Thread(r).start();

    try {
      // 等待10ms，否则服务器会视为攻击
      Thread.sleep(10);
    } catch (InterruptedException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    }
  }

  protected void localRenderTiles(Collection<Tile> tiles, Graphics2D g2d, ReferencedEnvelope viewportExtent, AffineTransform worldToImageTransform) {

    g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);

    for (Tile tile : tiles) {
      ReferencedEnvelope nativeTileEnvelope = tile.getExtent();
      ReferencedEnvelope tileEnvViewport;
      try {
        tileEnvViewport = nativeTileEnvelope.transform(viewportExtent.getCoordinateReferenceSystem(), true);
      } catch (TransformException | FactoryException e) {
        throw new RuntimeException(e);
      }
      double[] points = new double[4];

      points[0] = tileEnvViewport.getMinX();
      points[1] = tileEnvViewport.getMaxY();
      points[2] = tileEnvViewport.getMaxX();
      points[3] = tileEnvViewport.getMinY();

      worldToImageTransform.transform(points, 0, points, 0, 2);

      renderTile(tile, g2d, points);
    }
  }

}
